package com.tongandtang.security;

import com.alibaba.dubbo.config.annotation.Reference;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.tongandtang.bg.entity.Perm;
import com.tongandtang.bg.service.IUserService;
import com.tongandtang.utils.RedisUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;

@Component
public class PermCacheManager {

    private final static String GLOBAL_PERM_UPDATE_TIME="GPUD";
    private final static String PERM_KEY="PERM:";

    private final static String PERMS="PM";
    private final static String EXPIRED="EP";
    private final static String LAST_UPDATE="LU";

    @Reference
    private IUserService userService;

    @Autowired
    private RedisUtil redisUtil;

    @Autowired
    private ObjectMapper objectMapper;

    /**
     * 获得权限
     * @param userId
     * @return
     * @throws Exception
     */
    public List<Perm> getPerm(Long userId) throws Exception {
        String permKey=PERM_KEY+userId;
        if(redisUtil.hasKey(permKey)){
            //如果 标识为过期
            if(Boolean.parseBoolean((String)redisUtil.hGet(permKey,EXPIRED))
                    ||
                    //或者 从数据库取perms的时间小于全局权限更新时间
                    (redisUtil.hasKey(GLOBAL_PERM_UPDATE_TIME)
                            &&
                            Long.parseLong((String)redisUtil.hGet(permKey,LAST_UPDATE)) <Long.parseLong(redisUtil.get(GLOBAL_PERM_UPDATE_TIME))) ){
                //如果已经过期 则需重新从数据库读出权限
                return resetAndReturnPerm(userId);
            }else{
                JavaType javaType = objectMapper.getTypeFactory().constructParametricType(ArrayList.class,Perm.class);
                return objectMapper.readValue((String)redisUtil.hGet(permKey,PERMS),javaType);
            }
        }else{
            return resetAndReturnPerm(userId);
        }
    }

    /**
     * 从数据库中重新加载权限 到 缓存 并返回权限列表
     * @param userId
     * @return
     * @throws Exception
     */
    private List<Perm> resetAndReturnPerm(Long userId) throws Exception {
        String permKey=PERM_KEY+userId;
        List<Perm> perms=userService.getUserPerms(userId);
        redisUtil.hPut(permKey,PERMS,objectMapper.writeValueAsString(perms));
        redisUtil.hPut(permKey,EXPIRED,String.valueOf(false));
        redisUtil.hPut(permKey,LAST_UPDATE,String.valueOf(new Date().getTime()));
        return perms;
    }


    /**
     * 设置某个用户的缓存中的权限信息为 过期的
     * @param userId
     * @return
     * @throws Exception
     */
    public Boolean permIsExpired(Long userId) throws Exception {
        String permKey=PERM_KEY+userId;
        if(redisUtil.hasKey(permKey) ){
            return Boolean.parseBoolean((String)redisUtil.hGet(permKey,EXPIRED)) || ( redisUtil.hasKey(GLOBAL_PERM_UPDATE_TIME) && Long.parseLong((String)redisUtil.hGet(permKey,LAST_UPDATE)) <Long.parseLong(redisUtil.get(GLOBAL_PERM_UPDATE_TIME)) );
        }else{
            return true;
        }
    }


    /**
     * 设置权限过期
     * @param userId
     * @return
     * @throws Exception
     */
    public Boolean setPermExpired(Long userId) throws Exception {
        String permKey=PERM_KEY+userId;
        if(redisUtil.hasKey(permKey)){
            redisUtil.hPut(permKey,EXPIRED,String.valueOf(true));
            return true;
        }
        return false;
    }


    /**
     * 重置 全局权限 更新时间
     */
    public void setGlobalPermUpdateTime(){
        redisUtil.set(GLOBAL_PERM_UPDATE_TIME,String.valueOf(new Date().getTime()));
    }

}
